﻿using Castle.DynamicProxy;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tauren.Business.DLL.System_Log;
using Tauren.Business.DLL.TenantInformation;
using Tauren.Business.Entity.Const;
using Tauren.Business.Entity.Enum;
using Tauren.Business.Entity.Global;
using Tauren.Business.Entity.Input;
using Tauren.Business.Entity.Model;
using Tauren.Business.Infrastructure.Common;
using Tauren.Business.Service.System_Log;

namespace Tauren.Business.Core.ConfigExtension
{
    /// <summary>
    /// AOP拦截
    /// </summary>
    public class RequestInterceptor : IInterceptor
    {
        private readonly IHttpContextAccessor _accessor;
        private ISystemLogService logService;
        private ITenantInformationBusiness tBusiness;
        private readonly string[] ExceptMethod = new string[] {
            "tauren.business.service.system_log.systemlogservice"
            , "tauren.business.dll.system_log.systemlogbusiness",
            "tauren.business.service.system_log.isystemlogservice"
            , "tauren.business.dll.system_log.isystemlogbusiness" };
        private readonly string[] ExceptMethod2 = new string[] {
            "tauren.business.dll.tenantinformation.itenantinformationbusiness"
            ,"tauren.business.dll.tenantinformation.tenantinformationbusiness"  };

        private readonly string[] LogInMethod = new string[] {
            "Tauren.Business.Service.LogIns.LogInService"
            , "tauren.business.service.logins.loginservice",
        "tauren.business.dll.login.iloginbusiness",
        "tauren.business.dll.login.loginbusiness"};
        //支持单个写线程和多个读线程的锁
        private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();
        private static readonly string FileName = "AOPInterceptor-" + DateTime.Now.ToString("yyyyMMddHH") + ".log";
        /// <summary>
        /// 全局会话
        /// </summary>
        public GlobalModel Global { get; set; } = new GlobalModel();
        /// <summary>
        /// 构造哈数
        /// </summary>
        /// <param name="accessor"></param>
        public RequestInterceptor(IHttpContextAccessor accessor, IDbConnection _dbConnection)
        {
            _accessor = accessor ?? throw new ArgumentNullException(nameof(accessor));
            if (_accessor.HttpContext != null)
            {
                if (_accessor.HttpContext.Items.ContainsKey(GlobalConst.GolbalData) && !string.IsNullOrEmpty(_accessor.HttpContext.Items[GlobalConst.GolbalData] + ""))
                {
                    Global = JsonConvert.DeserializeObject<GlobalModel>(_accessor.HttpContext.Items[GlobalConst.GolbalData] + "");
                }
                Global.Language = (_accessor == null) ? "zh-cn" : string.IsNullOrEmpty(_accessor.HttpContext.Request.Headers["language"] + "") ? "zh-cn" : _accessor.HttpContext.Request.Headers["language"] + "";
            }
            logService = new SystemLogService(new SystemLogBusiness(_dbConnection));
            tBusiness = new TenantInformationBusiness(_dbConnection);

        }
        /// <summary>
        /// 进行拦截
        /// </summary>
        /// <param name="invocation"></param>
        public void Intercept(IInvocation invocation)
        {


            SystemLog model = GetSystemLog(invocation);
            //记录被拦截方法执行前的信息
            //string logData = $"【执行用户】：{Global.EmpName} \r\n" +
            //              $"【执行时间】：{DateTime.Now:yyyy/MM/dd HH:mm:ss}  \r\n" +
            //              $"【执行类名】: {invocation.TargetType.FullName}  \r\n" +
            //              $"【执行方法】:  {invocation.Method.Name}  \r\n" +
            //              $"【执行参数】：  {JsonConvert.SerializeObject(invocation.Arguments)} \r\n";
            try
            {
                //调用下一个拦截器直到目标方法
                invocation.Proceed();

                if (ExceptMethod.Any(o => o == invocation.TargetType.FullName.ToLower()) || (ExceptMethod2.Any(o => o == invocation.TargetType.FullName.ToLower())) && invocation.Method.Name.ToLower() == "getbycode") return;

                //判断是否为异步方法
                if (IsAsyncMethod(invocation.Method))
                {
                    var type = invocation.Method.ReturnType;
                    var resultProperty = type.GetProperty("Result");
                    if (resultProperty == null) return;
                    var result = resultProperty.GetValue(invocation.ReturnValue);
                    //logData += $"【执行完成】：{JsonConvert.SerializeObject(result)}";
                    //Parallel.For(0, 1, e =>
                    //{
                    //    WriteLog(new[] { logData });
                    //});
                    DistinguishLogModel(model, JsonConvert.SerializeObject(result), invocation);
                }
                else//同步方法
                {
                    //logData += $"【执行完成】：{JsonConvert.SerializeObject(invocation.ReturnValue)}";
                    //Parallel.For(0, 1, e =>
                    //{
                    //    WriteLog(new[] { logData });
                    //});
                    DistinguishLogModel(model, JsonConvert.SerializeObject(invocation.ReturnValue), invocation);
                }


            }
            catch (Exception ex)
            {
                //LogException(ex, logData);
                DistinguishLogModel(model, $"【出现异常】：{ex.Message + ex.InnerException}", invocation, LogType.Error);
                throw ex;
            }
        }

        //判断是否为异步方法
        private bool IsAsyncMethod(System.Reflection.MethodInfo method)
        {
            return method.ReturnType == typeof(Task) ||
                   method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>);
        }

        /// <summary>
        /// 日志写入方法
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="isHeader"></param>
        public static void WriteLog(string[] parameters, bool isHeader = true)
        {
            try
            {
                //进入写模式
                Lock.EnterWriteLock();

                //获取或创建文件夹
                var path = Path.Combine(Directory.GetCurrentDirectory(), "AOPLog");
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

                //获取log文件路径
                var logFilePath = Path.Combine(path, FileName);

                //转换及拼接字符
                var logContent = string.Join("\r\n", parameters);
                if (isHeader)
                {
                    logContent = "---------------------------------------\r\n"
                                 + DateTime.Now + "\r\n" + logContent + "\r\n";
                }

                //写入文件
                File.AppendAllText(logFilePath, logContent);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            finally
            {
                //退出写入模式，释放资源占用
                Lock.ExitWriteLock();
            }
        }

        //记录异常信息
        private void LogException(Exception ex, string logData)
        {
            if (ex == null) return;

            logData += $"【出现异常】：{ex.Message + ex.InnerException}\r\n";

            Parallel.For(0, 1, e =>
            {
                WriteLog(new[] { logData });
            });
        }

        /// <summary>
        /// 初始化类
        /// </summary>
        /// <param name="invocation"></param>
        /// <returns></returns>
        private SystemLog GetSystemLog(IInvocation invocation)
        {
            SystemLog model = new SystemLog();
            model.IPAddress = $"{_accessor.HttpContext.Connection.RemoteIpAddress + ""}:{_accessor.HttpContext.Connection.RemotePort + ""}";
            model.ServerAddress = $"{_accessor.HttpContext.Connection.LocalIpAddress + ""}:{_accessor.HttpContext.Connection.LocalPort + ""}";
            model.InterfacePath = $"{_accessor.HttpContext.Connection.LocalIpAddress + ""}:{_accessor.HttpContext.Connection.LocalPort + ""}{_accessor.HttpContext.Request.Path.ToString()}";
            model.ClientPath = _accessor.HttpContext.Request.Headers.ContainsKey("Referer") ? _accessor.HttpContext.Request.Headers["Referer"].ToString() : _accessor.HttpContext.Request.Headers.ContainsKey("Origin") ? _accessor.HttpContext.Request.Headers["Origin"].ToString() : "";
            model.UserAgent = _accessor.HttpContext.Request.Headers.ContainsKey("User-Agent") ? _accessor.HttpContext.Request.Headers["User-Agent"].ToString() : "";
            model.RequestParamtetr = JsonConvert.SerializeObject(invocation.Arguments);
            model.Creatdate = DateTime.Now;
            model.Modifdate = DateTime.Now;
            model.ExecuteClass = invocation.TargetType.FullName + "";
            model.ExecuteMethod = invocation.Method.Name + "";
            model.ExecuteParameter = JsonConvert.SerializeObject(invocation.Arguments);
            model.ExecuteDate = DateTime.Now;
            return model;
        }

        /// <summary>
        /// 判断是否是登录信息
        /// </summary>
        /// <param name="MethodName"></param>
        /// <returns></returns>
        private bool IsLogInMethod(string MethodName)
        {
            return LogInMethod.Any(o => o == MethodName.ToLower());
        }
        /// <summary>
        /// 进行日志添加
        /// </summary>
        /// <param name="model"></param>
        /// <param name="result"></param>
        /// <param name="invocation"></param>
        /// <param name="type"></param>
        private void DistinguishLogModel(SystemLog model, string result, IInvocation invocation, LogType type = LogType.Info)
        {
            if (IsLogInMethod(invocation.TargetType.FullName))
            {
                if (type == LogType.Info) type = LogType.LogInInfo;
                else type = LogType.LogInError;
                Parallel.For(0, 1, e =>
                {
                    GenerateInLoginModel(model, invocation, JsonConvert.SerializeObject(result), (int)type);
                });
            }
            else
            {
                Parallel.For(0, 1, e =>
                {
                    GenerateNotInLoginModel(model, JsonConvert.SerializeObject(result), (int)type);
                });
            }
        }
        /// <summary>
        /// 不是登录时的日志
        /// </summary>
        /// <param name="model"></param>
        /// <param name="content"></param>
        /// <param name="type"></param>
        private void GenerateNotInLoginModel(SystemLog model, string content, int type)
        {
            model.Account = Global.Account;
            model.CNName = Global.EmpName;
            model.Code = Global.TenementCode;
            model.Creator = Global.EmpId;
            model.EmpId = Global.EmpId;
            model.ENName = Global.EmpEnName;
            model.ExecuteCompletiontime = DateTime.Now;
            model.ExecuteResult = content;
            model.Language = Global.Language;
            model.LogType = type;
            model.Modifier = Global.EmpId;
            model.OrgId = Global.OrgId;
            model.OrgName = Global.OrgName;
            model.Phone = Global.TenementPhone;
            model.PositionId = Global.PositionId;
            model.PositionName = Global.PositionName;
            model.TCnname = Global.TenementName;
            model.UserId = Global.UserId;
            model.EmpCode = Global.EmpCode;
            logService.Add(model);
        }
        /// <summary>
        /// 不是登录时的日志
        /// </summary>
        /// <param name="model"></param>
        /// <param name="content"></param>
        /// <param name="type"></param>
        private void GenerateInLoginModel(SystemLog model, IInvocation invocation, string content, int type)
        {
            var body = _accessor.HttpContext.Request.Body;
            string code = "";
            LogInUser logInUser = GetLogInModel(invocation, out code);
            Entity.Model.TenantInformation tenantInformation = tBusiness.GetByCode(code);

            model.Account = logInUser.Account;
            model.Code = code;
            model.ExecuteCompletiontime = DateTime.Now;
            model.ExecuteResult = content;
            model.Language = Global.Language;
            model.LogType = type;
            if (tenantInformation != null)
            {
                model.CNName = "";
                model.Creator = -1;
                model.EmpId = -1;
                model.ENName = "";
                model.Modifier = -1;
                model.OrgId = -1;
                model.OrgName = "";
                model.Phone = tenantInformation.Phone;
                model.PositionId = -1;
                model.PositionName = "";
                model.TCnname = tenantInformation.CNName;
                model.UserId = -1;
                model.EmpCode = "";
            }
            logService.Add(model);
        }

        private LogInUser GetLogInModel(IInvocation invocation, out string code)
        {
            List<LogInModel> models = JsonConvert.DeserializeObject<List<LogInModel>>(JsonConvert.SerializeObject(invocation.Arguments));
            LogInModel logInModel = models.FirstOrDefault();
            string UserInfo = logInModel.Content.AESDecrypt(logInModel.Key);
            code = logInModel.TenantCode;
            return UserInfo.ToEntity<LogInUser>();
        }
        /// <summary>
        /// 获取请求参数
        /// </summary>
        /// <returns></returns>
        private string GetRequestBody()
        {
            string postJson = _accessor.HttpContext.Request.QueryString.Value;
            // 获取请求参数
            _accessor.HttpContext.Request.EnableBuffering();
            var stream = _accessor.HttpContext.Request.Body;
            long? length = _accessor.HttpContext.Request.ContentLength;
            if (length != null && length > 0)
            {
                //byte[] buffer = new byte[_accessor.HttpContext.Request.ContentLength.Value];
                //stream.Position = 0L;
                //stream.ReadAsync(buffer, 0, buffer.Length);
                //postJson = Encoding.UTF8.GetString(buffer);
                foreach (var key in _accessor.HttpContext.Request.Form.Keys)
                {
                    postJson += key + ":" + _accessor.HttpContext.Request.Form[key].ToString() + " ";
                }
            }
            return postJson;
        }
    }
}
